/*
 * ChessPane.java
 *
 * Created on 18. �erven 2007, 10:51
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package cz.possoft.client.chess.gui.components;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;

import sun.awt.image.ToolkitImage;
import cz.possoft.client.gui.gamepanel.components.GamePane;
import cz.possoft.client.utils.RGBHolder;
import cz.possoft.serializable.chess.figurky.Figurka;
import cz.possoft.serializable.chess.figurky.King;
import cz.possoft.serializable.chess.figurky.Pesec;
import cz.possoft.serializable.chess.figurky.Prazdna;
import cz.possoft.serializable.chess.figurky.Queen;
import cz.possoft.serializable.chess.figurky.Rider;
import cz.possoft.serializable.chess.figurky.Shooter;
import cz.possoft.serializable.chess.figurky.Tower;
import cz.possoft.serializable.chess.game.ChessPole;
import cz.possoft.serializable.generic.HraciPole;
import cz.possoft.serializable.generic.Location;

/**
 * Hraci pole pro hru sachy
 * 
 * @author mara
 */
public class ChessPane extends GamePane implements Runnable {

	// do pole bitmapa vrazime vsechny rgb souradnice aktualniho obrazku.
	// bitmapa je pouzita pro mizeni obrazku, bitmapa2 pro objevovani
	private RGBHolder[][] bitmapa = new RGBHolder[55][55];
	private RGBHolder[][] bitmapa2 = new RGBHolder[55][55];

	// v tomto poli je rozdil ,ktery pricitame v jednom kroku "mizeni" k pixelum v bitmapa
	// bitmapaRozd je pouzita pro mizeni obrazku, bitmapaRozd2 pro objevovani
	private RGBHolder[][] bitmapaRozd = new RGBHolder[55][55];
	private RGBHolder[][] bitmapaRozd2 = new RGBHolder[55][55];

	// souradnice dilciho pracovniho obrazku se kterym se prave dela prekreslovani
	private int xd = -1;
	private int yd = -1;
	
	private int xd2 = -1;
	private int yd2 = -1;	

	// indikator, zda je prave vlakno spusteno. Pouzity pro mizeni i objevovani
	private boolean runFlag = false;	
	
	// vlakno patrici k tomuto chessPane
	private Thread thisThread;

	// Obrazky vsech figurek
	private Image blackKingBlack;
	private Image blackKingWhite;
	private Image whiteKingBlack;
	private Image whiteKingWhite;

	private Image blackQueenBlack;
	private Image blackQueenWhite;
	private Image whiteQueenBlack;
	private Image whiteQueenWhite;

	private Image blackTowerBlack;
	private Image blackTowerWhite;
	private Image whiteTowerBlack;
	private Image whiteTowerWhite;

	private Image blackRiderBlack;
	private Image blackRiderWhite;
	private Image whiteRiderBlack;
	private Image whiteRiderWhite;

	private Image blackShooterBlack;
	private Image blackShooterWhite;
	private Image whiteShooterBlack;
	private Image whiteShooterWhite;

	private Image blackPesecBlack;
	private Image blackPesecWhite;
	private Image whitePesecBlack;
	private Image whitePesecWhite;

	private Image blackPrazdna;
	private Image whitePrazdna;
	
	// interval vlakna na vykreslovani mizeni a objevovani figurek
	private static final int SLEEP_INTERVAL = 40;	
	
	// dilci pracovni obrazek
	// workingImage je pouzita pro mizeni obrazku, workingImage2 pro objevovani
	private BufferedImage workingImage;			
	private BufferedImage workingImage2;
	
	// true pokud obrazek prave mizi nebo je vlakno neaktivni. False pokud se prave obrazek objevuje.
	private boolean miziciIndicator = true;

	// priznak udava, jestli je aktivni hrac bily nebo cerny (potreba kvuli
	// otoceni sachovnice)
	private boolean isWhitePlayer;

	// pri prekreslovani je figurka na teto lokaci prekreslena az pozdeji
	private Location dontPaintLocation = new Location(-1, -1).setNull();
	
	// pri prekreslovani je tato figurka nakreslena namisto figurky z sachovnice
	// - sachovnice ma uz totiz pri
	// prekreslovani novou hodnotu figurky, ale my musime pri postupnem
	// prekreslovani jeste nakreslit starou figurku.
	private Figurka dontPaintOldFigurka;

	public void setDontPaintLocation(Location dpl) {
		dontPaintLocation = dpl;
	}

	public void setDontPaintOldFigurka(Figurka dpl) {
		dontPaintOldFigurka = dpl;
	}

	public void setWhitePlayer(boolean isWhitePlayer) {
		this.isWhitePlayer = isWhitePlayer;
	}

	// kvuli java reflection
	public ChessPane() {
	}

	/** Creates a new instance of ChessPane */
	public void init(HraciPole hPole) {
		thisThread = new Thread(this);
		thisThread.start();
		loadImages();
		addMouseMotionListener(new ChessMouseMotionListener());
		addMouseListener(new MouseExitListener());
		setHraciPole(hPole);
	}

	// metoda nacte vsecny obrazky do promennych image
	private void loadImages() {
		try {
			blackKingBlack = new ImageIcon("figurky/blackKing-black.GIF")
					.getImage();
			blackKingWhite = new ImageIcon("figurky/blackKing-white.GIF")
					.getImage();
			whiteKingBlack = new ImageIcon("figurky/whiteKing-black.GIF")
					.getImage();
			whiteKingWhite = new ImageIcon("figurky/whiteKing-white.GIF")
					.getImage();

			blackQueenBlack = new ImageIcon("figurky/blackQueen-black.GIF")
					.getImage();
			blackQueenWhite = new ImageIcon("figurky/blackQueen-white.GIF")
					.getImage();
			whiteQueenBlack = new ImageIcon("figurky/whiteQueen-black.GIF")
					.getImage();
			whiteQueenWhite = new ImageIcon("figurky/whiteQueen-white.GIF")
					.getImage();

			blackTowerBlack = new ImageIcon("figurky/blackTower-black.GIF")
					.getImage();
			blackTowerWhite = new ImageIcon("figurky/blackTower-white.GIF")
					.getImage();
			whiteTowerBlack = new ImageIcon("figurky/whiteTower-black.GIF")
					.getImage();
			whiteTowerWhite = new ImageIcon("figurky/whiteTower-white.GIF")
					.getImage();

			blackRiderBlack = new ImageIcon("figurky/blackRider-black.GIF")
					.getImage();
			blackRiderWhite = new ImageIcon("figurky/blackRider-white.GIF")
					.getImage();
			whiteRiderBlack = new ImageIcon("figurky/whiteRider-black.GIF")
					.getImage();
			whiteRiderWhite = new ImageIcon("figurky/whiteRider-white.GIF")
					.getImage();

			blackShooterBlack = new ImageIcon("figurky/blackShooter-black.GIF")
					.getImage();
			blackShooterWhite = new ImageIcon("figurky/blackShooter-white.GIF")
					.getImage();
			whiteShooterBlack = new ImageIcon("figurky/whiteShooter-black.GIF")
					.getImage();
			whiteShooterWhite = new ImageIcon("figurky/whiteShooter-white.GIF")
					.getImage();

			blackPesecBlack = new ImageIcon("figurky/blackPesec-black.GIF")
					.getImage();
			blackPesecWhite = new ImageIcon("figurky/blackPesec-white.GIF")
					.getImage();
			whitePesecBlack = new ImageIcon("figurky/whitePesec-black.GIF")
					.getImage();
			whitePesecWhite = new ImageIcon("figurky/whitePesec-white.GIF")
					.getImage();

			blackPrazdna = new ImageIcon("figurky/blackPole.GIF").getImage();
			whitePrazdna = new ImageIcon("figurky/whitePole.GIF").getImage();
		} catch (Exception ex) {
			System.out.println("Exception pri nacitani image figurek.");
			ex.printStackTrace();
		}
	}

	// metoda run bezi stale, ale priznak runFlag je true jen pokud se prave
	// prekresluje figurka (mizi nebo se objevuje)
	public void run() {
		int count = 0;
		while (true) {							
					try {
						// sleep 200 pokud je vlakno neaktivni - neprobiha mizeni nebo objevovani figurky						
						//Thread.sleep(200);
						synchronized(thisThread) {
						      thisThread.wait();
						}
					} catch (Exception e) {
						e.printStackTrace();
					}			        
					while (runFlag) {	
						
						// rozlisujeme, jestli probiha mizeni nebo objevovani figurky. Kvuli tomu jsou zde priznaky
						BufferedImage workingImg = miziciIndicator ? workingImage : workingImage2;	
						RGBHolder[][] bitmapa = miziciIndicator ? this.bitmapa : this.bitmapa2;
						RGBHolder[][] bitmapaRozd = miziciIndicator ? this.bitmapaRozd : this.bitmapaRozd2;
						try {
							// sleep 15 pokud je vlakno aktivni - probiha mizeni nebo objevovani figurky	
							Thread.sleep(SLEEP_INTERVAL);
						} catch (Exception e) {
							e.printStackTrace();
						}											
					if (runFlag) {
						// postupne mizime nebo objevujeme obrazek. K bitmape pricitame rozdil (bitmapaRozd)
						for (int i = 0; i < 55; i++)
							for (int j = 0; j < 55; j++) {
								workingImg.setRGB(i, j, bitmapa[i][j]
										.addRGBHolderToThis(bitmapaRozd[i][j])
										.getRGB());
							}
						count++;

						// mizeni nebo objevovani figurky prave skoncilo
						if (count == RGBHolder.COUNT) {
							dontPaintLocation.setNull();
							
							// tento pripad nastava, pokud prave skoncilo zmizeni figurky
							if (miziciIndicator) {
								xd = -1;
								yd = -1;
							}
							// tento pripad nastava, pokud prave skoncilo objevovani figurky 
							// runFlag se da na false jen v pripade, ze uz zmizela figurka a take se zaroven objevila
							// vlakno se tim v podstate dostane do neaktivniho stavu.
							else {
								xd2 = -1;
								yd2 = -1;
								runFlag = false;								
							}
							count = 0;						
							miziciIndicator = !miziciIndicator;							
						}
						repaint();
					} 
					}
		}
	}

	// nakresli figurku na souradnici x,y. Nakresli se odpovidajici figurka z
	// objektu sachovnice
	private void paintFigurka(int x, int y, Graphics g) {

		// zjistime figurku a jeji obrazek
		Figurka fig = ((ChessPole) getHraciPole()).getFigurky()[x][y];
		Image img = getImageForPaint(x, y, false);

		// nakreslime figurku spolu s obrazkem
		g.drawImage(img, getXForPaint(x), getYForPaint(y), this);
		if (fig.isRing()) {
			g.setColor(Color.RED);
			g.drawRect(getXForPaint(x), getYForPaint(y), 54, 54);
		}
		if (fig.isMarkedForPlay()) {
			g.setColor(Color.BLUE);
			g.drawRect(getXForPaint(x), getYForPaint(y), 54, 54);
		}

	}

	// Nakresleni sachovnice
	public void paint(Graphics g) {
		super.paint(g);
		g.setColor(Color.RED);
		g.fillRect(0, 0, 450, 450);

		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++) {
				paintFigurka(i, j, g);
			}
	}

	// metoda bude postupne prekreslovat figurku na pozicich x, y tim, ze
	// figurka bude postupne mizet. Po kazdem dilcim mizeni se vola repaint.
	// metoda slouzi k inicializaci mizeni a aktivaci vlakna pro mizeni
	public void postupneZmizniFigurku(int x, int y) {
		xd = -1;
		yd = -1;

		// do workingImage se zkopiruje obrazek z x,y a jeho souradnice do
		// xd,yd. Novy obrazek se musi naklonovat z puvodniho
		Image img = getImageForPaint(x, y, true);		
		workingImage = getBufferedImageFromToolkit(img);		
		xd = x;
		yd = y;		

		// zjisteni zakladni barvy policka
		RGBHolder baseHolder = new RGBHolder(workingImage.getRGB(2, 2));
		
		// Toto je fake, protoze pri nacitani z obrazku na cernym (hnedym) poli
		// sachovnice to blblo a nacetlo se spatne
		if (baseHolder.getR() != 0) {
			baseHolder = new RGBHolder(152, 102, 102);
		}

		// inicializace pracovniho obrazku
		// Do bitmapaRozd se ulozi holder, ktery se pricita v kazdem kroku mizeni
		for (int i = 0; i < 55; i++)
			for (int j = 0; j < 55; j++) {
				int pixel = workingImage.getRGB(i, j);
				bitmapa[i][j] = new RGBHolder(pixel);
				bitmapaRozd[i][j] = RGBHolder.getRGBDifferenceForBaseAndPixel(
						baseHolder, bitmapa[i][j]);
			}
		runFlag = true;
		synchronized(thisThread) {
		     thisThread.notify();
		}
	}

	// metoda bude postupne prekreslovat figurku na pozicich x, y tim, ze
	// figurka bude postupne objevovana a stara figurka bude postupne mizet.
	// metoda slouzi k inicializaci objevovani a aktivaci vlakna, ktere je vsak stejne aktivovano v dusledku predchoziho
	// spusteni mizeni. Tato metoda je totiz spustena z ChessPanel.doPlay v momente, kdy uz mizeni figurky probiha.
	public void postupneObjevFigurku(int x, int y) {
		xd2 = -1;
		yd2 = -1;		

		// do workingImage2 se zkopiruje puvodni obrazek z x,y a jeho souradnice do
		// xd,yd. Novy obrazek 'newImage' obsahuje figurku, ktera se bude na tomto policku objevovat.		
		Image img = getImageForPaint(x, y, true);		
    	workingImage2 = getBufferedImageFromToolkit(img);		  
		Image img2 = getImageForPaint(x, y, false);
		BufferedImage newImage = getBufferedImageFromToolkit(img2);
		xd2 = x;
		yd2 = y;

		// zjisteni zakladni barvy policka
		RGBHolder baseHolder = new RGBHolder(workingImage2.getRGB(2, 2));
		// Toto je fake, protoze pri nacitani z obrazku na cernym (hnedym) poli
		// sachovnice to blblo a nacetlo se spatne
		if (baseHolder.getR() != 0) {
			baseHolder = new RGBHolder(152, 102, 102);
		}

		// bitmapa2 obsahuje puvodni obrazek a postupne se bude menit k novemu.
		// Rozdil v jednom kroku je bitmapaRozd2.		
		for (int i = 0; i < 55; i++)
			for (int j = 0; j < 55; j++) {
				bitmapa2[i][j] = new RGBHolder(workingImage2.getRGB(i,j));
				bitmapaRozd2[i][j] = RGBHolder.getRGBDifferenceForBaseAndPixel(
						new RGBHolder(newImage.getRGB(i, j)), bitmapa2[i][j]);
			}
		runFlag = true;
	}

	// Vraci bod, kde se zacina kreslit pro lokaci X na sachovnici z intervalu
	// 1-8.
	// Sachovnice ma pro bileho a cerneho hrace stejnou hodnotu, ale musime
	// menit kresleni podle toho, jestli hrajeme
	// za cerneho nebo bileho hrace (otoceni sachovnice).
	private int getXForPaint(int x) {
		if (isWhitePlayer) {
			return 4 + x * 55;
		} else {
			return 4 + (7 - x) * 55;
		}
	}

	// Vraci bod, kde se zacina kreslit pro lokaci Y na sachovnici z intervalu
	// 1-8.
	// Sachovnice ma pro bileho a cerneho hrace stejnou hodnotu, ale musime
	// menit kresleni podle toho, jestli hrajeme
	// za cerneho nebo bileho hrace (otoceni sachovnice).
	private int getYForPaint(int y) {
		if (isWhitePlayer) {
			return 4 + y * 55;
		} else {
			return 4 + (7 - y) * 55;
		}
	}

	// vraci obrazek figurky na zaklade x,y. Pri postupneMizeni = true je brano v potaz, zda mame vratit
	// figurku z sachovnice nebo figurku z dontPaintLocation, kde je stara figurka
	private Image getImageForPaint(int x, int y, boolean postupneMizeni) {
		// vracime pracovni obrazek, pokud zrovna prekreslujeme zpracovavanou
		// figurku.
		if ((x == xd) && (y == yd)) {
			return workingImage;
		}
		if ((x == xd2) && (y == yd2)) {
			return workingImage2;
		}		

		boolean cerna;
		if ((x + y) % 2 == 1)
			cerna = true;
		else
			cerna = false;

		// zjistime figurku a jeji obrazek
		Figurka fig = ((ChessPole) getHraciPole()).getFigurky()[x][y];

		// pro policko, na ktere ma prave najet figurka, se musi vykreslit
		// puvodni figurka
		if ((dontPaintLocation.getX() == x) && (dontPaintLocation.getY() == y) && (postupneMizeni)) {				
			fig = dontPaintOldFigurka;
		}

		if ((fig instanceof Prazdna) && (cerna))
			return blackPrazdna;
		if ((fig instanceof Prazdna) && !(cerna))
			return whitePrazdna;

		// KRESLENI CERNYCH FIGUREK
		if (fig.getColor() == 1) {

			if ((fig instanceof Pesec) && (cerna))
				return blackPesecBlack;
			if ((fig instanceof Pesec) && !(cerna))
				return blackPesecWhite;

			if ((fig instanceof Tower) && (cerna))
				return blackTowerBlack;
			if ((fig instanceof Tower) && !(cerna))
				return blackTowerWhite;

			if ((fig instanceof Rider) && (cerna))
				return blackRiderBlack;
			if ((fig instanceof Rider) && !(cerna))
				return blackRiderWhite;

			if ((fig instanceof Shooter) && (cerna))
				return blackShooterBlack;
			if ((fig instanceof Shooter) && !(cerna))
				return blackShooterWhite;

			if ((fig instanceof Queen) && (cerna))
				return blackQueenBlack;
			if ((fig instanceof Queen) && !(cerna))
				return blackQueenWhite;

			if ((fig instanceof King) && (cerna))
				return blackKingBlack;
			if ((fig instanceof King) && !(cerna))
				return blackKingWhite;
		}
		if (fig.getColor() == 2) { // KRESLENI BILYCH FIGUREK
			if ((fig instanceof Pesec) && (cerna))
				return whitePesecBlack;
			if ((fig instanceof Pesec) && !(cerna))
				return whitePesecWhite;

			if ((fig instanceof Tower) && (cerna))
				return whiteTowerBlack;
			if ((fig instanceof Tower) && !(cerna))
				return whiteTowerWhite;

			if ((fig instanceof Rider) && (cerna))
				return whiteRiderBlack;
			if ((fig instanceof Rider) && !(cerna))
				return whiteRiderWhite;

			if ((fig instanceof Shooter) && (cerna))
				return whiteShooterBlack;
			if ((fig instanceof Shooter) && !(cerna))
				return whiteShooterWhite;

			if ((fig instanceof Queen) && (cerna))
				return whiteQueenBlack;
			if ((fig instanceof Queen) && !(cerna))
				return whiteQueenWhite;

			if ((fig instanceof King) && (cerna))
				return whiteKingBlack;
			if ((fig instanceof King) && !(cerna))
				return whiteKingWhite;

		}
		// tato moznost by teoreticky nemela nastat
		return null;
	}

	// z toolkitImage (ikona) zjisti bufferedImage a vraci jeho novou instanci
	// (naklonovany obrazek)
	private BufferedImage getBufferedImageFromToolkit(Image img) {
		BufferedImage source;

		// pri rosade a en-passant se muze stat, ze je img BufferedImage
		if (img instanceof ToolkitImage) {
		    source = ((ToolkitImage) img).getBufferedImage();
		} else {
			source = (BufferedImage)img;
		}
		BufferedImage novyImage = new BufferedImage(source.getWidth(), source
				.getHeight(), source.getType());
		Graphics g = novyImage.getGraphics();
		g.drawImage(source, 0, 0, null);
		return novyImage;
	}

}
